﻿
using System;
using System.Collections.Generic;
using Xunit;
using Xunit.Extensions;

namespace EmperialApps.WeatherSpark.Data {

    public class TestEphemerides {

        private const double H = 60;
        private static readonly DateTimeOffset DateM7 = new DateTimeOffset( 2012, 8, 12, 0, 0, 0, TimeSpan.FromHours( -7 ) );
        private static readonly DateTimeOffset DateM6 = new DateTimeOffset( 2012, 8, 12, 0, 0, 0, TimeSpan.FromHours( -6 ) );

        private static double Days( DateTimeOffset date ) {
            TimeSpan offset = date - default( DateTimeOffset );
            return offset.TotalDays;
        }

        private static DateTimeOffset SecondsToNone( DateTimeOffset date ) {
            return date
                .AddSeconds( -date.Second )
                .AddMilliseconds( -date.Millisecond );
        }


        private const string JulianDateValues_Name = "JulianDateValues";
        public static IEnumerable<object[]> JulianDateValues {
            get {
                yield return new object[] { DateM7, 2456151.79 };
                yield return new object[] { new DateTimeOffset( 2000, 1, 1, 12, 0, 0, default( TimeSpan ) ), 2451545.0 };
            }
        }

        [Theory]
        [PropertyData( JulianDateValues_Name )]
        public void GetJulianDate_GivenValidDate_ReturnsExpectedResult( DateTimeOffset gregorian, double julian ) {
            double actual = Ephemerides.GetJulianDate( gregorian );

            Assert.Equal( julian, actual, 2 );
        }


        private const string DaylightValues_Name = "DaylightValues";
        public static IEnumerable<object[]> DaylightValues {
            get {
                yield return new object[] { new Coordinate( 40.02, -105.28 ), DateM7, DateM7.AddHours( 5 + 10 / H ), DateM7.AddHours( 19 + 1 / H ) };
                yield return new object[] { new Coordinate( 30.27, -97.74 ), DateM6, DateM6.AddHours( 5 + 56 / H ), DateM6.AddHours( 19 + 15 / H ) };
            }
        }

        [Theory]
        [PropertyData( DaylightValues_Name )]
        public void CalculateDaylight_GivenValidValues_ReturnsExpectedResult( Coordinate location, DateTimeOffset date, DateTimeOffset sunrise, DateTimeOffset sunset ) {
            var expected = Pair.Create( sunrise, sunset );

            var daylight = Ephemerides.CalculateDaylight( location, date );
            var actual = Pair.Create(
                SecondsToNone( daylight.Item1 ),
                SecondsToNone( daylight.Item2 ) );

            Assert.Equal( expected, actual );
        }

        [Theory]
        [InlineData( 71.27, -156.80, -9.0 )]
        [InlineData( -55.94, -67.28, -3.5 )]
        [InlineData( -77.85, 166.67, +12.0 )]
        public void CalculateDaylight_GivenValuesWithExtremeLatitude_DoesNotThrow( double latitude, double longitude, double offset ) {
            var location = new Coordinate( latitude, longitude );
            var date = DateTimeOffset.Now;

            Assert.DoesNotThrow( ( ) => Ephemerides.CalculateDaylight( location, date ) );
        }

    }

}
